package com.angle3d;

import com.angle3d.VertexBuffer.VertexAttribute;

/**
 * Utility class providing static methods to create {@link Mesh} for most common geometries.
 * @author alessandro
 *
 */
public class MeshUtil {
	
	/**
	 * Creates and returns a Mesh representing a simple quad shape.
	 * Each vertex contains position, normal and texture coordinates.
	 * The quad is aligned with the local X-Y plane; the normals are orthogonal to such a plane, i.e. parallel to Z.
	 * @param sx the quad size in the X direction
	 * @param sy the quad size in the Y direction
	 * @return a Mesh representing a quad
	 */
	public static Mesh createQuad(float sx, float sy) {
		float[] vertices = new float[] {
				-0.5f*sx, -0.5f*sy, 0,    0, 0, 1,    0, 0,
				 0.5f*sx, -0.5f*sy, 0,    0, 0, 1,    1, 0,
				 0.5f*sx,  0.5f*sy, 0,    0, 0, 1,    1, 1,
				-0.5f*sx,  0.5f*sy, 0,    0, 0, 1,    0, 1
		};
		
		VertexBuffer vb = new VertexBuffer(6, 4, VertexAttribute.DEFAULT_POSITION, VertexAttribute.DEFAULT_NORMAL, VertexAttribute.DEFAULT_TEXCOORD);
		vb.setIndices(new short[] {0,1,2,2,3,0});
		vb.setVertices(vertices);
		
		Mesh mesh = new Mesh();
		mesh.setVertexBuffer(vb);
		mesh.setPrimitive(Mesh.Primitive.TRIANGLES);
		BoundingBox box = mesh.getBoundingBox();
		box.minX = -0.5f*sx;
		box.minY = -0.5f*sy;
		box.minZ = 0;
		box.maxX = 0.5f*sx;
		box.maxY = 0.5f*sy;
		box.maxZ = 0;
		return mesh;
	}
	
	/**
	 * Creates and returns a Mesh representing a simple box shape.
	 * Each vertex contains position, normal and texture coordinates.
	 * @param sx the quad size in the X direction
	 * @param sy the quad size in the Y direction
	 * @param sz the quad size in the Z direction
	 * @return a Mesh representing a quad
	 */
	public static Mesh createBox(float sx, float sy, float sz) {
		float[] vertices = new float[] {
				 //face +z
				-0.5f*sx, -0.5f*sy,  0.5f*sz,    0, 0, 1,    0, 0,
				 0.5f*sx, -0.5f*sy,  0.5f*sz,    0, 0, 1,    1, 0,
				 0.5f*sx,  0.5f*sy,  0.5f*sz,    0, 0, 1,    1, 1,
				-0.5f*sx,  0.5f*sy,  0.5f*sz,    0, 0, 1,    0, 1,
				 //face -z
				-0.5f*sx,  0.5f*sy, -0.5f*sz,    0, 0,-1,    0, 0,
				 0.5f*sx,  0.5f*sy, -0.5f*sz,    0, 0,-1,    1, 0,
				 0.5f*sx, -0.5f*sy, -0.5f*sz,    0, 0,-1,    1, 1,
				-0.5f*sx, -0.5f*sy, -0.5f*sz,    0, 0,-1,    0, 1,
				 //face +x
				 0.5f*sx, -0.5f*sy, -0.5f*sz,    1, 0, 0,    0, 0,
				 0.5f*sx,  0.5f*sy, -0.5f*sz,    1, 0, 0,    1, 0,
				 0.5f*sx,  0.5f*sy,  0.5f*sz,    1, 0, 0,    1, 1,
				 0.5f*sx, -0.5f*sy,  0.5f*sz,    1, 0, 0,    0, 1,
				//face -x
				-0.5f*sx,  0.5f*sy, -0.5f*sz,   -1, 0, 0,    0, 0,
				-0.5f*sx, -0.5f*sy, -0.5f*sz,   -1, 0, 0,    1, 0,
				-0.5f*sx, -0.5f*sy,  0.5f*sz,   -1, 0, 0,    1, 1,
				-0.5f*sx,  0.5f*sy,  0.5f*sz,   -1, 0, 0,    0, 1,
				//face +y
				 0.5f*sx,  0.5f*sy, -0.5f*sz,    0, 1, 0,    0, 0,
				-0.5f*sx,  0.5f*sy, -0.5f*sz,    0, 1, 0,    1, 0,
				-0.5f*sx,  0.5f*sy,  0.5f*sz,    0, 1, 0,    1, 1,
				 0.5f*sx,  0.5f*sy,  0.5f*sz,    0, 1, 0,    0, 1,
				//face -y
				-0.5f*sx, -0.5f*sy, -0.5f*sz,    0,-1, 0,    0, 0,
				 0.5f*sx, -0.5f*sy, -0.5f*sz,    0,-1, 0,    1, 0,
				 0.5f*sx, -0.5f*sy,  0.5f*sz,    0,-1, 0,    1, 1,
				-0.5f*sx, -0.5f*sy,  0.5f*sz,    0,-1, 0,    0, 1,
		};
		
		short[] indices = new short[] {
				 0,  1,  2,  2,  3,  0,
				 4,  5,  6,  6,  7,  4,
				 8,  9, 10, 10, 11,  8,
				12, 13, 14, 14, 15, 12,
				16, 17, 18, 18, 19, 16,
				20, 21, 22, 22, 23, 20
		};
		
		VertexBuffer vb = new VertexBuffer(36, 24, VertexAttribute.DEFAULT_POSITION, VertexAttribute.DEFAULT_NORMAL, VertexAttribute.DEFAULT_TEXCOORD);
		vb.setIndices(indices);
		vb.setVertices(vertices);
		
		Mesh mesh = new Mesh();
		mesh.setVertexBuffer(vb);
		mesh.setPrimitive(Mesh.Primitive.TRIANGLES);
		BoundingBox box = mesh.getBoundingBox();
		box.minX = -0.5f*sx;
		box.minY = -0.5f*sy;
		box.minZ = -0.5f*sz;
		box.maxX = 0.5f*sx;
		box.maxY = 0.5f*sy;
		box.maxZ = 0.5f*sz;
		return mesh;
	}
	
	public static Mesh createLineSet(float[] points) {
		int pointCount = points.length/3;
		VertexBuffer vb = new VertexBuffer(pointCount, pointCount, VertexAttribute.DEFAULT_POSITION);
		vb.setVertices(points);
		short[] indices = new short[pointCount];
		for (short i = 0; i < indices.length; ++i) {
			indices[i] = i;
		}
		vb.setIndices(indices);
		
		Mesh mesh = new Mesh();
		mesh.setVertexBuffer(vb);
		mesh.setPrimitive(Mesh.Primitive.LINES);
		//update bounding box
		float minX = Float.MAX_VALUE;
		float minY = Float.MAX_VALUE;
		float minZ = Float.MAX_VALUE;
		float maxX = Float.MIN_VALUE;
		float maxY = Float.MIN_VALUE;
		float maxZ = Float.MIN_VALUE;
		for (int i = 0; i < pointCount; ++i) {
			float x = points[3*i+0];
			float y = points[3*i+1];
			float z = points[3*i+2];
			if (x < minX) minX = x;
			if (y < minY) minY = y;
			if (z < minZ) minZ = z;
			if (x > maxX) maxX = x;
			if (y > maxY) maxY = y;
			if (z > maxZ) maxZ = z;
		}
		BoundingBox bb = mesh.getBoundingBox();
		bb.minX = minX;
		bb.minY = minY;
		bb.minZ = minZ;
		bb.maxX = maxX;
		bb.maxY = maxY;
		bb.maxZ = maxZ;
		return mesh;
	}
	
	public static Mesh createPolyLine(float[] points) {
		int pointCount = points.length/3;
		VertexBuffer vb = new VertexBuffer(pointCount, pointCount, VertexAttribute.DEFAULT_POSITION);
		vb.setVertices(points);
		short[] indices = new short[pointCount];
		for (short i = 0; i < indices.length; ++i) {
			indices[i] = i;
		}
		vb.setIndices(indices);
		
		Mesh mesh = new Mesh();
		mesh.setVertexBuffer(vb);
		mesh.setPrimitive(Mesh.Primitive.LINE_STRIP);
		//update bounding box
		float minX = Float.MAX_VALUE;
		float minY = Float.MAX_VALUE;
		float minZ = Float.MAX_VALUE;
		float maxX = Float.MIN_VALUE;
		float maxY = Float.MIN_VALUE;
		float maxZ = Float.MIN_VALUE;
		for (int i = 0; i < pointCount; ++i) {
			float x = points[3*i+0];
			float y = points[3*i+1];
			float z = points[3*i+2];
			if (x < minX) minX = x;
			if (y < minY) minY = y;
			if (z < minZ) minZ = z;
			if (x > maxX) maxX = x;
			if (y > maxY) maxY = y;
			if (z > maxZ) maxZ = z;
		}
		BoundingBox bb = mesh.getBoundingBox();
		bb.minX = minX;
		bb.minY = minY;
		bb.minZ = minZ;
		bb.maxX = maxX;
		bb.maxY = maxY;
		bb.maxZ = maxZ;
		return mesh;
	}
	
	public static Mesh createPoints(float[] points) {
		int pointCount = points.length/3;
		VertexBuffer vb = new VertexBuffer(pointCount, pointCount, VertexAttribute.DEFAULT_POSITION);
		vb.setVertices(points);
		short[] indices = new short[pointCount];
		for (short i = 0; i < indices.length; ++i) {
			indices[i] = i;
		}
		vb.setIndices(indices);
		
		Mesh mesh = new Mesh();
		mesh.setVertexBuffer(vb);
		mesh.setPrimitive(Mesh.Primitive.POINTS);
		//update bounding box
		float minX = Float.MAX_VALUE;
		float minY = Float.MAX_VALUE;
		float minZ = Float.MAX_VALUE;
		float maxX = Float.MIN_VALUE;
		float maxY = Float.MIN_VALUE;
		float maxZ = Float.MIN_VALUE;
		for (int i = 0; i < pointCount; ++i) {
			float x = points[3*i+0];
			float y = points[3*i+1];
			float z = points[3*i+2];
			if (x < minX) minX = x;
			if (y < minY) minY = y;
			if (z < minZ) minZ = z;
			if (x > maxX) maxX = x;
			if (y > maxY) maxY = y;
			if (z > maxZ) maxZ = z;
		}
		BoundingBox bb = mesh.getBoundingBox();
		bb.minX = minX;
		bb.minY = minY;
		bb.minZ = minZ;
		bb.maxX = maxX;
		bb.maxY = maxY;
		bb.maxZ = maxZ;
		return mesh;
	}

}
